﻿
using System;
using System.Threading.Tasks;
using System.Diagnostics;

namespace WSU.ChekaParallel.TestTask
{
    static class TestCompletionSource
    {
        private static void TestSetResult()
        {
            TaskCompletionSource<int> completeSource = new TaskCompletionSource<int>();
            Task<int> task = completeSource.Task;

            // start a timer which will set the result sometimes later
            System.Timers.Timer timer = new System.Timers.Timer { Interval = 1500, AutoReset = false };
            timer.Elapsed += (sender, evtargs) =>
                                 {
                                     completeSource.SetResult(8888);
                                 };
            timer.Start();

            // block to wait for the result
            Console.WriteLine("blocking and waiting for the result, ......");
            Console.WriteLine("finally, result is set to be: {0}", task.Result);
        }

        private static void TestSetException()
        {
            const string ErrorMsg = "test";
            Task exceptionTask = DelayedException(1000, ErrorMsg);
            Console.WriteLine("future token returned without blocking.");

            try
            {
                exceptionTask.Wait();
            }
            catch (Exception ex)
            {
                // chekanote: what we caught is not the exception we set into the TaskCompletionSource
                // but is the AggregatedException, its InnerException is just what we set into that TaskCompletionSource
                Debug.Assert(ex is AggregateException);

                Exception innerException = ((AggregateException)ex).InnerException;
                Debug.Assert(innerException is InvalidOperationException);
                Debug.Assert(ErrorMsg.Equals(innerException.Message));

                Console.WriteLine("error message = {0}", innerException.Message);
            }
        }

        private static Task DelayedException(int interval, string errorMsg)
        {
            TaskCompletionSource<object> tcs = new TaskCompletionSource<object>();

            System.Timers.Timer timer = new System.Timers.Timer { Interval = interval, AutoReset = false };

            System.Timers.ElapsedEventHandler timeoutCallback = null;
            timeoutCallback = (sender, evtargs) =>
                                  {
                                      timer.Elapsed -= timeoutCallback;
                                      timer.Dispose();
                                      tcs.SetException(new InvalidOperationException(errorMsg));
                                  };
            timer.Elapsed += timeoutCallback;
            timer.Start();

            return tcs.Task;
        }

        private static void StartDelayedFunction<TResult>(this TaskCompletionSource<TResult> completeSource, int interval, TResult result)
        {
            var timer = new System.Timers.Timer { Interval = interval, AutoReset = false };
            System.Timers.ElapsedEventHandler timeoutCallback = null;
            timeoutCallback = (sender, evtargs) =>
            {
                timer.Elapsed -= timeoutCallback;
                timer.Dispose();
                completeSource.SetResult(result);
            };
            timer.Elapsed += timeoutCallback;
            timer.Start();
        }

        #region "TaskCompletionSource with return type"

        private static Task<int> AwaitFuncDemo(int interval, int input)
        {
            TaskCompletionSource<int> completeSource = new TaskCompletionSource<int>();

            // ----------------- perform non-block operations here
            completeSource.StartDelayedFunction(interval, input * input);

            return completeSource.Task;
        }

        private static void TestWaitResult<TResult>(Task<TResult> futureResult)
        {
            Console.WriteLine("await function returns, never blocked");
            Console.WriteLine("future result eventually comes: {0}", futureResult.Result);
        }

        #endregion

        #region "TaskCompletionSource without return type"

        private static Task AwaitActionDemo(int interval)
        {
            TaskCompletionSource<object> completeSource = new TaskCompletionSource<object>();

            // ----------------- perform non-block operations here
            completeSource.StartDelayedFunction(interval, null);

            return completeSource.Task;// Task<object> can be casted to Task
        }

        private static void TestWaitAction(Task future)
        {
            Console.WriteLine("future action returns with a token, never blocked");
            future.Wait();
            Console.WriteLine("async method eventually completes.");
        }

        #endregion

        public static void TestMain()
        {
            // TestSetResult();
            TestSetException();
            // TestWaitResult(AwaitFuncDemo(2000, 6));
            // TestWaitAction(AwaitActionDemo(1500));
        }
    }
}